home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
X User Tools
/
X User Tools (O'Reilly and Associates)(1994).ISO
/
sources
/
xman
/
man.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-27
|
22KB
|
899 lines
/*
* xman - X Window System manual page display program.
*
* $XConsortium: man.c,v 1.29 91/07/30 22:03:20 rws Exp $
*
* Copyright 1987, 1988 Massachusetts Institute of Technology
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. M.I.T. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* Author: Chris D. Peterson, MIT Project Athena
* Created: August 10, 1987
*/
#include "globals.h"
#include "vendor.h" /* vendor-specific defines and data */
#ifndef X_NOT_POSIX
#include <dirent.h>
#else
#ifdef SYSV
#include <dirent.h>
#else
#ifdef USG
#include <dirent.h>
#else
#include <sys/dir.h>
#ifndef dirent
#define dirent direct
#endif
#endif
#endif
#endif
#ifdef DEBUG
static char error_buf[BUFSIZ]; /* The buffer for error messages. */
#endif /* DEBUG */
static void SortList(), ReadMandescFile(), SortAndRemove(), InitManual();
static void AddToCurrentSection();
static void ReadCurrentSection();
#define SECT_ERROR -1
/* Function Name: Man
* Description: Builds a list of all manual directories and files.
* Arguments: none.
* Returns: the number of manual sections.
*/
int
Man()
{
SectionList *list = NULL;
char *ptr, manpath[BUFSIZ], *path, *current_label;
int sect, num_alloced;
/*
* Get the environment variable MANPATH, and if it doesn't exist then use
* SYSMANPATH and LOCALMANPATH.
*/
ptr = getenv("MANPATH");
if (ptr == NULL || streq(ptr , "") ) {
strcpy(manpath, SYSMANPATH);
#ifdef LOCALMANPATH
strcat(manpath, ":");
strcat(manpath, LOCALMANPATH);
#endif
} else {
strcpy(manpath, ptr);
}
/*
* Get the list of manual directories in the users MANPATH that we should
* open to look for manual pages. The ``mandesc'' file is read here.
*/
for ( path = manpath ; (ptr = index(path , ':')) != NULL ; path = ++ptr) {
*ptr = '\0';
ReadMandescFile(&list, path);
}
ReadMandescFile(&list, path);
SortList(&list);
sect = 0;
num_alloced = SECTALLOC;
manual = (Manual *) XtMalloc( sizeof(Manual) * num_alloced );
InitManual( manual, list->label );
manual[sect].flags = list->flags;
current_label = NULL;
while ( list != NULL ) {
SectionList * old_list;
if ( current_label == NULL || streq(list->label, current_label) )
AddToCurrentSection( manual + sect, list->directory);
else {
if (manual[sect].nentries == 0) { /* empty section, re-use it. */
XtFree(manual[sect].blabel);
manual[sect].blabel = list->label;
manual[sect].flags = list->flags;
}
else {
if ( ++sect >= num_alloced ) {
num_alloced += SECTALLOC;
manual = (Manual *) realloc ( (char *) manual,
(sizeof(Manual) * num_alloced));
if (manual == NULL)
PrintError("Could not allocate memory for manual sections.");
}
InitManual( manual + sect, list->label );
manual[sect].flags = list->flags;
}
AddToCurrentSection( manual + sect, list->directory);
}
/* Save label to see if it matches next entry. */
current_label = list->label;
old_list = list;
list = list->next;
XtFree((char *) old_list); /* free what you allocate. */
}
if (manual[sect].nentries != 0)
sect++; /* don't forget that last section. */
SortAndRemove(manual, sect);
#ifdef notdef /* dump info. */
DumpManual(sect);
#endif
/*
* realloc manual to be minimum space necessary.
*/
manual = (Manual *) realloc( (char *) manual, (sizeof(Manual) * sect));
if (manual == NULL)
PrintError("Could not allocate memory for manual sections.");
return(sect); /* return the number of man sections. */
}
/* Function Name: SortList
* Description: Sorts the list of sections to search.
* Arguments: list - a pointer to the list to sort.
* Returns: a sorted list.
*
* This is the most complicated part of the entire operation.
* all sections with the same label must by right next to each other,
* but the sections that are in the standard list have to come first.
*/
static void
SortList(list)
SectionList ** list;
{
SectionList * local;
SectionList *head, *last, *inner, *old;
if (*list == NULL)
PrintError("No manual sections to read, exiting.");
/*
* First step
*
* Look for standard list items, and more them to the top of the list.
*/
last = NULL; /* keep Saber happy. */
for ( local = *list ; local->next != NULL ; local = local->next) {
if ( local->flags ) {
if ( local == *list ) /* top element is already standard. */
break;
head = local;
/* Find end of standard block */
for ( ; (local->next != NULL) && (local->flags)
; old = local, local = local->next);
last->next = old->next; /* Move the block. */
old->next = *list;
*list = head;
break; /* First step accomplished. */
}
last = local;
}
/*
* Second step
*
* Move items with duplicate labels right next to each other.
*/
local = *list;
for ( local = *list ; local->next != NULL ; local = local->next) {
inner = local->next;
while ( inner != NULL) {
if ( streq(inner->label, local->label) && (inner != local->next)) {
last->next = inner->next; /* Move it to directly follow local. */
inner->next = local->next;
local->next = inner;
inner = last; /* just so that we keep marching down the
tree (this keeps us from looping). */
}
last = inner;
inner = inner->next;
}
}
}
/* Function Name: ReadMandescFile
* Description: Reads the mandesc file, and adds more sections as
* nescessary.
* Arguments: path - path name if the current search directory.
* section_list - pointer to the list of sections.
* Returns: TRUE in we should use default sections
*/
static void
ReadMandescFile( section_list, path )
SectionList ** section_list;
char * path;
{
char mandesc_file[BUFSIZ]; /* full path to the mandesc file. */
FILE * descfile;
char string[BUFSIZ], local_file[BUFSIZ];
Boolean use_defaults = TRUE;
char *cp;
sprintf(mandesc_file, "%s/%s", path, MANDESC);
if ( (descfile = fopen(mandesc_file, "r")) != NULL) {
while ( fgets(string, BUFSIZ, descfile) != NULL) {
string[strlen(string)-1] = '\0'; /* Strip off the CR. */
if ( streq(string, NO_SECTION_DEFAULTS) ) {
use_defaults = FALSE;
continue;
}
if ((cp = index(string,'\t')) != NULL) {
char *s;
*cp++ = '\0';
strcpy(local_file, MAN);
strcat(local_file, string);
if ((s = index(cp,'\t')) != NULL) {
*s++ = '\0';
if (streq(s, SUFFIX))
AddNewSection(section_list, path, local_file, cp, MSUFFIX);
else if (streq(s, FOLD))
AddNewSection(section_list, path, local_file, cp, MFOLD);
else if (streq(s, FOLDSUFFIX))
AddNewSection(section_list, path, local_file, cp, MFOLDSUFFIX);
else
AddNewSection(section_list, path, local_file, cp, MNULL);
} else
AddNewSection(section_list, path, local_file, cp, MNULL);
} else {
sprintf(local_file, "%s%c", MAN, string[0]);
AddNewSection(section_list, path, local_file, (string + 1), FALSE );
}
}
fclose(descfile);
}
if (use_defaults)
AddStandardSections(section_list, path);
}
/* Function Name: AddNewSection
* Description: Adds the new section onto the current section list.
* Arguments: list - pointer to the section list.
* path - the path to the current manual section.
* file - the file to save.
* label - the current section label.
* flags = 1 - add a suffix
* = 2 - fold to lower case
* Returns: none.
*/
void
AddNewSection(list, path, file, label, flags)
SectionList **list;
char * path, * label, * file;
int flags;
{
SectionList * local_list, * end;
char full_path[BUFSIZ];
/* Allocate a new list element */
local_list = (SectionList *) XtMalloc(sizeof(SectionList));
if (*list != NULL) {
for ( end = *list ; end->next != NULL ; end = end->next );
end->next = local_list;
}
else
*list = local_list;
local_list->next = NULL;
local_list->label = StrAlloc(label);
sprintf(full_path, "%s/%s", path, file);
local_list->directory = StrAlloc(full_path);
local_list->flags = flags;
}
/* Function Name: AddToCurrentSection
* Description: This function gets the names of the manual page
* directories, then closes the directory.
* Arguments: local_manual - a pointer to a manual pages structure.
* path - the path to this directory.
* Returns: none.
*/
static void
AddToCurrentSection(local_manual, path)
Manual * local_manual;
char * path;
{
char temp_path[BUFSIZ];
ReadCurrentSection(local_manual, path);
sprintf(temp_path, "%s.%s", path, COMPRESSION_EXTENSION);
ReadCurrentSection(local_manual, temp_path);
}
/* Function Name: ReadCurrentSection
* Description: Actually does the work of adding entries to the
* new section
* Arguments: local_manual - a pointer to a manual pages structure.
* path - the path to this directory.
* compressed - Is this a compressed directory?
* Returns: TRUE if any entries are found.
*/
static void
ReadCurrentSection(local_manual, path)
Manual * local_manual;
char * path;
{
DIR * dir;
register struct dirent *dp;
register int nentries;
register int nalloc;
char full_name[BUFSIZ], *ptr;
if((dir = opendir(path)) == NULL) {
#ifdef DEBUG
sprintf(error_buf,"Can't open directory %s", path);
PopupWarning(NULL, error_buf);
#endif /* DEBUG */
return;
}
/*
* Remove the compression extension from the path name.
*/
if ( (ptr = rindex(path, '.')) != NULL)
if (streq(ptr + 1, COMPRESSION_EXTENSION))
*ptr = '\0';
nentries = local_manual->nentries;
nalloc = local_manual->nalloc;
while( (dp = readdir(dir)) != NULL ) {
char * name = dp->d_name;
if (name[0] == '.')
continue;
#ifndef CRAY
if (index(name, '.') == NULL)
continue;
#endif
if( nentries >= nalloc ) {
nalloc += ENTRYALLOC;
local_manual->entries =(char **) XtRealloc((char *)local_manual->entries,
nalloc * sizeof(char *));
local_manual->entries_less_paths =
(char **) XtRealloc((char *)local_manual->entries_less_paths,
nalloc * sizeof(char *));
}
sprintf(full_name, "%s/%s", path, name);
/*
* Remove the compression extension from the entry name.
*/
if ( (ptr = rindex(full_name, '.')) != NULL)
if (streq(ptr + 1, COMPRESSION_EXTENSION))
*ptr = '\0';
local_manual->entries[nentries] = StrAlloc(full_name);
local_manual->entries_less_paths[nentries] =
rindex(local_manual->entries[nentries], '/');
if ( local_manual->entries_less_paths[nentries] == NULL )
PrintError("Internal error while cataloging manual pages.");
++ nentries;
}
local_manual->nentries = nentries;
local_manual->nalloc = nalloc;
closedir(dir);
}
/* Function Name: SortAndRemove
* Description: This function sorts all the entry names and
* then removes all the duplicate entries.
* Arguments: man - a pointer to the manual structure.
* number - the number of manual sections.
* Returns: an improved manual stucure
*/
static void
SortAndRemove(man, number)
Manual *man;
int number;
{
int i,j;
char *l1, *l2, **s1;
for ( i = 0; i < number; man++, i++) { /* sort each section */
register int j = 0;
#ifdef DEBUG
printf("sorting section %d - %s\n", i, man->blabel);
#endif /* DEBUG */
s1 = (char **)malloc(man->nentries * sizeof(char *));
/* temporarily remove suffixes of entries, preventing them from */
/* being used in alpabetic comparison ie sccs-delta.1 vs sccs.1 */
for (j=0; j<man->nentries; j++)
if ((s1[j] = rindex(man->entries_less_paths[j], '.')) != NULL)
*s1[j] = '\0';
sortstrs ( man->entries_less_paths, man->nentries, man->entries );
/* put back suffixes */
for (j=0; j<man->nentries; j++)
if (s1[j] != NULL) *s1[j] = '.';
free(s1);
#ifdef DEBUG
printf("removing from section %d.\n", i);
#endif /* DEBUG */
{
register int j, k, nent, nentm1;
int j2;
nent = man -> nentries;
nentm1 = nent - 1;
j = 0;
l2 = man->entries_less_paths[j++];
if ( l2 == NULL )
PrintError("Internal error while removing duplicate manual pages.");
while ( j < nentm1 )
{
l1 = l2;
l2 = man->entries_less_paths[j++];
if ( l2 == NULL )
PrintError("Internal error while removing duplicate manual pages."
);
if ( streq(l1,l2) )
{
j2 = j-1;
k = j2;
while ( j < nent )
{
man -> entries_less_paths[k] = man -> entries_less_paths[j];
man -> entries[k++] = man -> entries[j++];
}
j = j2;
-- man -> nentries;
-- nent;
-- nentm1;
}
}
}
}
}
/*
******* Replacement for qsort to keep
******* identical entries in order
A somewhat ugly hack of something that was once simpler...
*/
/*
Sort an array of pointers to strings, keeping it
in ascending order by (1) string comparison and
(2) original entry order in the pointer array.
This is a modified radix exchange algorithm.
In case there's insufficient memory for a temporary copy
of the pointer array, the original order of identical strings
isn't preserved.
*/
#ifndef Byte
#define Byte unsigned char
#endif
#ifndef reg
#define reg register
#endif
sortstrs ( data, size, otherdata ) /* Sort an array of string ptrs */
Byte *data[];
int size;
Byte *otherdata[];
{
Byte **sp, **ep;
Byte **othersp, **otherep;
int *origorder;
origorder = (int *) calloc (size, sizeof(int));
if ( origorder )
{
reg int i;
for ( i=0; i < size; ++i )
origorder[i] = i;
}
sp = data;
ep = &data[size-1];
othersp = otherdata;
otherep = &otherdata[size-1];
if ( origorder )
{
sortstrs_block_oo ( sp, ep, 0, 0x80, origorder, &origorder[size-1],
othersp, otherep );
free (origorder);
}
else
sortstrs_block ( sp, ep, 0, 0x80, othersp, otherep );
}
/*---------------------------------*/
/* Sort 1 block of data on 1 bit */
/*---------------------------------*/
sortstrs_block ( start, end, offset, mask, otherstart, otherend )
Byte **start;
Byte **end;
int offset;
Byte mask;
Byte **otherstart;
Byte **otherend;
{
reg Byte **sp, **ep;
reg Byte m;
reg int off;
reg Byte *t;
reg int curstrlen;
int maxstrlen;
Byte **othersp, **otherep;
#define newstring(ptr) \
{ \
t = *ptr; \
curstrlen = 0; \
while ( *t++ ) ++ curstrlen; \
if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
t = *ptr; \
}
maxstrlen = 0;
sp = start;
ep = end;
off = offset;
m = mask;
othersp = otherstart;
otherep = otherend;
while (1)
{
newstring(sp)
while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
{
++ sp;
++ othersp;
newstring(sp)
}
if ( sp == ep )
break;
newstring(ep);
while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
{
-- ep;
-- otherep;
newstring(ep)
}
if ( sp == ep )
break;
t = *sp;
*sp = *ep;
*ep = t;
t = *othersp;
*othersp = *otherep;
*otherep = t;
}
t = *sp;
if ((curstrlen < off) || ((t[off] & m) == 0))
{
if ( ep != end )
{
++ ep;
++ otherep;
}
}
else
{
if ( sp != start )
{
-- sp;
-- othersp;
}
}
m >>= 1;
if ( m == 0 )
{
m = 0x80;
if ( ++off >= maxstrlen )
return;
}
if ( sp != start )
sortstrs_block ( start, sp, off, m, otherstart, othersp );
if ( ep != end )
sortstrs_block ( ep, end, off, m, otherep, otherend );
}
/*-----------------------------------------------------------------*/
/* Sort 1 block of data on 1 bit; check for out-of-order entries */
/*-----------------------------------------------------------------*/
sortstrs_block_oo ( start, end, offset, mask, ostart, oend,
otherstart, otherend )
Byte **start;
Byte **end;
int offset;
Byte mask;
int *ostart;
int *oend;
Byte **otherstart;
Byte **otherend;
{
reg Byte **sp, **ep;
reg int *osp, *oep;
reg Byte m;
reg int off;
reg Byte *t;
reg int u;
reg int curstrlen;
int maxstrlen;
Byte **othersp, **otherep;
#define newstring(ptr) \
{ \
t = *ptr; \
curstrlen = 0; \
while ( *t++ ) ++ curstrlen; \
if ( curstrlen > maxstrlen ) maxstrlen = curstrlen; \
t = *ptr; \
}
maxstrlen = 0;
sp = start;
ep = end;
osp = ostart;
oep = oend;
off = offset;
m = mask;
othersp = otherstart;
otherep = otherend;
while (1)
{
newstring(sp)
while (((sp != ep) && ((curstrlen < off) || ((t[off] & m) == 0))))
{
++ sp;
++ osp;
++ othersp;
newstring(sp)
}
if ( sp == ep )
break;
newstring(ep);
while (((sp != ep) && (curstrlen >= off) && ((t[off] & m) != 0)))
{
-- ep;
-- oep;
-- otherep;
newstring(ep)
}
if ( sp == ep )
break;
t = *sp;
*sp = *ep;
*ep = t;
t = *othersp;
*othersp = *otherep;
*otherep = t;
u = *osp;
*osp = *oep;
*oep = u;
}
t = *sp;
if ((curstrlen < off) || ((t[off] & m) == 0))
{
if ( ep != end )
{
++ ep;
++ oep;
++ otherep;
}
}
else
{
if ( sp != start )
{
-- sp;
-- osp;
-- othersp;
}
}
m >>= 1;
if ( m == 0 )
{
m = 0x80;
if ( ++off >= maxstrlen ) /* Finished sorting block of strings: */
{ /* Restore duplicates to
riginal order */
reg Byte **cp;
reg int *ocp;
Byte **othercp;
if ( sp != start )
{
cp = start;
ocp = ostart;
othercp = otherstart;
while ( cp != sp )
{
if ( *ocp > *(ocp+1) )
{
t = *(cp+1);
*(cp+1) = *cp;
*cp = t;
t = *(othercp+1);
*(othercp+1) = *othercp;
*othercp = t;
u = *(ocp+1);
*(ocp+1) = *ocp;
*ocp = u;
if ( cp != start )
{
-- cp;
-- ocp;
-- othercp;
continue;
}
}
++ cp;
++ ocp;
++ othercp;
}
}
if ( ep != end )
{
cp = ep;
ocp = oep;
othercp = otherep;
while ( cp != end )
{
if ( *ocp > *(ocp+1) )
{
t = *(cp+1);
*(cp+1) = *cp;
*cp = t;
t = *(othercp+1);
*(othercp+1) = *othercp;
*othercp = t;
u = *(ocp+1);
*(ocp+1) = *ocp;
*ocp = u;
if ( cp != ep )
{
-- cp;
-- ocp;
-- othercp;
continue;
}
}
++ cp;
++ ocp;
++ othercp;
}
}
return;
}
}
if ( sp != start )
sortstrs_block_oo ( start, sp, off, m, ostart, osp, otherstart, othersp );
if ( ep != end )
sortstrs_block_oo ( ep, end, off, m, oep, oend, otherep, otherend );
}
/* Function Name: InitManual
* Description: Initializes this manual section.
* Arguments: l_manual - local copy of the manual structure.
* label - the button label for this section.
* Returns: none.
*/
static void
InitManual(l_manual, label)
Manual * l_manual;
char * label;
{
bzero( l_manual, sizeof(Manual) ); /* clear it. */
l_manual->blabel = label; /* set label. */
}
#if defined(DEBUG)
/* Function Name: DumpManual
* Description: Debugging function that dumps the entire manual page
* structure.
* Arguments: number - the number of sections.
* Returns: none.
*/
DumpManual(number)
{
register int i,j;
for ( i = 0; i < number; i++) {
printf("label: %s\n", manual[i].blabel);
for (j = 0; j < manual[i].nentries; j++)
printf("%s\n", manual[i].entries[j]);
}
}
#endif /* DEBUG */